<?php
// @formatter:off
/**
 * @file WCContainer.abs.php
 * @author Alejandro Dario Simi
 * @date $Date: 2013-06-04 03:02:53 +0000 (Tue, 04 Jun 2013) $
 *
 * $Id: WCContainer.abs.php 68 2013-06-04 03:02:53Z daemonraco@gmail.com $
 * $URL: http://wcomix.googlecode.com/svn/tags/wcomix-1.0-BETA1/includes/containers/WCContainer.abs.php $
 */
// @formatter:on

/**
 * @todo each element should load independently and not all at once.
 */

/**
 * @abstract
 * @class WCContainer
 * This class represents a container for objects managed by certain
 * WCBasicHolder. It logic allows to access this objects in many way, like by id,
 * by name and others.
 */
class WCContainer {
	/**
	 * @var WCHolder This is the pointer to the objects holder to use.
	 */
	protected $_holder = false;
	/**
	 * @var int Holds the count of items inside the container.
	 */
	protected $_count = 0;
	/**
	 * @var WCRepresentation[] This is the list of held objects.
	 */
	protected $_items = array();
	protected $_names = array();
	/**
	 * @var boolean
	 */
	protected $_unloadedItems = false;
	/**
	 * @var boolean
	 */
	protected $_loadInvalids = false;

	//
	// Public methods.
	public function addId($id) {
		//
		// Enforce integer value.
		$id += 0;
		//
		// Checking that the id is not already added.
		if(!isset($this->_items[$id])) {
			//
			// Adding the id as false to delay the loading of the real
			// object.
			$this->_items[$id] = false;
			//
			// 'Cause it was added, as false, there at least one
			// unloaded item.
			$this->_unloadedItems = true;
			//
			// Internal count must be incremented.
			$this->_count++;
		}
	}
	public function addItem($id, &$item) {
		//
		// Enforce integer value.
		$id += 0;
		//
		// Checking that the id is not already added.
		if(!isset($this->_items[$id])) {
			//
			// Adding the id associated with the item, this won't
			// require a delayed loading of the object.
			$this->_items[$id] = $item;
			//
			// Internal count must be incremented.
			$this->_count++;
		}
	}
	public function count() {
		return $this->_count;
	}
	public function ids() {
		return array_keys($this->_items);
	}
	public function item($id) {
		$this->loadItem($id);
		return isset($this->_items[$id]) ? $this->_items[$id] : null;
	}
	public function items() {
		$this->loadUnloadedItems();
		return $this->_items;
	}
	/**
	 * @param string $name
	 * @return int
	 */
	public function name($name) {
		$this->loadUnloadedItems();
		return isset($this->_names[$name]) && isset($this->_names[$name][0]) ? $this->_names[$name][0] : null;
	}
	/**
	 * @param string $name
	 * @return int[]
	 */
	public function nameList($name) {
		$this->loadUnloadedItems();
		return isset($this->_names[$name]) ? $this->_names[$name] : array();
	}
	/**
	 * @param string $name
	 * @return string[]
	 */
	public function names($name) {
		$this->loadUnloadedItems();
		return array_keys($this->_names);
	}
	public function removeId($id) {
		//
		// Enforce integer value.
		$id += 0;
		//
		// If it doesn't exists, it wasn't added. Then no action is
		// required.
		if(isset($this->_items[$id])) {
			//
			// If is different than false, it may appear on names'
			// lists.
			if($this->_items[$id] !== false) {
				//
				// Searching for the name with the id.
				foreach($this->_names as $name => $ids) {
					//
					// Checking if the current name has the id.
					if(in_array($id, $ids)) {
						//
						// Removing the id.
						$this->_names[$name] = array_diff($this->_names[$name], array($id));
						//
						// When found this loop stops.
						break;
					}
				}
			}
			//
			// Removing it from items list.
			unset($this->_items[$id]);

			$this->_count--;
		}
	}
	public function reset() {
		unset($this->_items);
		unset($this->_names);

		$this->_items = array();
		$this->_names = array();
		$this->_unloadedItems = true;
		$this->_count = 0;
	}
	//
	// Protected methods.
	protected function addItemToNames($id) {
		//
		// Only when the object is ok, it is added to names lists.
		// @warning At this point item is supposed to be loaded.
		if($this->_items[$id]->ok()) {
			$name = $this->_items[$id]->name();
			if(!isset($this->_names[$name])) {
				$this->_names[$name][] = $id;
			}
		}
	}
	protected function basicConstructor(WCHolder &$holder, $invalids = false) {
		$this->_holder = $holder;
		$this->_loadInvalids = $invalids;
	}
	public function basicDestructor() {
		unset($this->_items);
		unset($this->_names);
	}
	protected function loadUnloadedItems() {
		if($this->_unloadedItems) {
			foreach($this->_items as $id => $value) {
				if($value === false) {
					$this->loadItem($id);
				}
			}

			$this->_count = count($this->_items);
			$this->_unloadedItems = true;
		}
	}
	protected function loadItem($id) {
		//
		// Checking that it's added and not yet loaded. Otherwise, nothing
		// is done.
		if(isset($this->_items[$id]) && $this->_items[$id] === false) {
			$aux = $this->loadItemFromHolder($id);
			if($aux && ($aux->ok() || $this->_loadInvalids)) {
				$this->_items[$id] = $aux;
				$this->addItemToNames($id);
			} else {
				unset($this->_items[$id]);
			}
		}
	}
	protected function loadItemFromHolder($id) {
		die(__CLASS__."::".__FUNCTION__."(): Subclass Responsibillity.");
		return false;
	}
}
?>